using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using ILRuntime.CLR.Method;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;
using UnityEngine;

public class HotFixMgr
{
    public static ILRuntime.Runtime.Enviorment.AppDomain appdomain;

    /// <summary>
    /// ����dll��pdb
    /// </summary>
    /// <param name="dll"></param>
    /// <param name="pdb"></param>
    public static void LoadHotFixAssembly(byte[] dll, byte[] pdb)
    {
        appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
        MemoryStream m_fs = new MemoryStream(dll);
        MemoryStream m_p = new MemoryStream(pdb);
        appdomain.LoadAssembly(m_fs, m_p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
        
        appdomain.DebugService.StartDebugService(80);

        OnILRuntimeInitialized();

        //绑定注册 (最后执行)
        ILRuntime.Runtime.Generated.CLRBindings.Initialize(appdomain);

        //appdomain.Invoke("Unity.Hotfix.InitUnityClass", "Init", null, null);
    }

    /// <summary>
    /// ILRunitme Initialized
    /// </summary>
    static void OnILRuntimeInitialized()
    {
#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
        appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif
        appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((action) =>
        {
            return new UnityEngine.Events.UnityAction(() =>
            {
                ((System.Action)action)();
            });
        });

        SetupCLRRedirectionAddComponent();//设置AddComponent的重定向
        SetupCLRRedirectionGetComponent();//设置GetComponent的重定向

        //注册重定向函数
        RegisterCLR();

        //默认委托注册仅仅支持系统自带的Action以及Function
        RegisterDelegateManager();

        //注册适配器
        RegisterAdaptor();


        LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appdomain);
    }

    /// <summary>
    /// 注册重定向函数
    /// </summary>
    static void RegisterCLR()
    {
        List<Type> list = new List<Type>();
        // 注册重定向函数
        list.Add(typeof(Dictionary<int, ILTypeInstance>));
        list.Add(typeof(Dictionary<int, int>));
        list.Add(typeof(Dictionary<object, object>));
        list.Add(typeof(Dictionary<int, object>));
        list.Add(typeof(Dictionary<long, object>));
        list.Add(typeof(Dictionary<long, int>));
        list.Add(typeof(Dictionary<int, long>));
        list.Add(typeof(Dictionary<string, long>));
        list.Add(typeof(Dictionary<string, int>));
        list.Add(typeof(Dictionary<string, object>));
        list.Add(typeof(Dictionary<string, GameObject>));
        list.Add(typeof(List<ILTypeInstance>));
        list.Add(typeof(List<int>));
        list.Add(typeof(List<long>));
        list.Add(typeof(List<string>));
        list.Add(typeof(List<object>));
        
    }

    static void RegisterDelegateManager()
    {
        // 注册委托
        appdomain.DelegateManager.RegisterMethodDelegate<List<object>>();
        appdomain.DelegateManager.RegisterMethodDelegate<object>();
        appdomain.DelegateManager.RegisterMethodDelegate<bool>();
        appdomain.DelegateManager.RegisterMethodDelegate<string>();
        appdomain.DelegateManager.RegisterMethodDelegate<float>();
        appdomain.DelegateManager.RegisterMethodDelegate<long, int>();
        appdomain.DelegateManager.RegisterMethodDelegate<long, MemoryStream>();
        appdomain.DelegateManager.RegisterMethodDelegate<long, IPEndPoint>();
        appdomain.DelegateManager.RegisterMethodDelegate<ILTypeInstance>();
        appdomain.DelegateManager.RegisterMethodDelegate<AsyncOperation>();
        appdomain.DelegateManager.RegisterMethodDelegate<GameObject>();
        appdomain.DelegateManager.RegisterMethodDelegate<Dictionary<string, object>>();


        appdomain.DelegateManager.RegisterFunctionDelegate<UnityEngine.Events.UnityAction>();
        appdomain.DelegateManager.RegisterFunctionDelegate<ILTypeInstance, bool>();
        appdomain.DelegateManager.RegisterFunctionDelegate<System.Collections.Generic.KeyValuePair<System.String, System.Int32>, System.String>();
        appdomain.DelegateManager.RegisterFunctionDelegate<System.Collections.Generic.KeyValuePair<System.Int32, System.Int32>, System.Boolean>();
        appdomain.DelegateManager.RegisterFunctionDelegate<System.Collections.Generic.KeyValuePair<System.String, System.Int32>, System.Int32>();
        appdomain.DelegateManager.RegisterFunctionDelegate<List<int>, int>();
        appdomain.DelegateManager.RegisterFunctionDelegate<List<int>, bool>();
        appdomain.DelegateManager.RegisterFunctionDelegate<int, bool>();//Linq
        appdomain.DelegateManager.RegisterFunctionDelegate<int, int, int>();//Linq
        appdomain.DelegateManager.RegisterFunctionDelegate<KeyValuePair<int, List<int>>, bool>();
        appdomain.DelegateManager.RegisterFunctionDelegate<KeyValuePair<int, int>, KeyValuePair<int, int>, int>();


        appdomain.DelegateManager.RegisterFunctionDelegate<int, string>();
        appdomain.DelegateManager.RegisterMethodDelegate<int>();
        appdomain.DelegateManager.RegisterMethodDelegate<System.String, UnityEngine.Object, System.Object, System.Object, System.Object>();

        appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((act) =>
        {
            return new UnityEngine.Events.UnityAction(() =>
            {
                ((Action)act)();
            });
        });

        appdomain.DelegateManager.RegisterDelegateConvertor<Comparison<KeyValuePair<int, int>>>((act) =>
        {
            return new Comparison<KeyValuePair<int, int>>((x, y) =>
            {
                return ((Func<KeyValuePair<int, int>, KeyValuePair<int, int>, int>)act)(x, y);
            });
        });
    }

    /// <summary>
    /// 注册适配器
    /// </summary>
    static void RegisterAdaptor()
    {
        //跨域继承的注册
        appdomain.RegisterCrossBindingAdaptor(new InheritanceAdapter());
        //注册协程适配器
        appdomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
        //注册Mono适配器
        appdomain.RegisterCrossBindingAdaptor(new MonoBehaviourAdapter());
        //注册Window适配器
        //appdomain.RegisterCrossBindingAdaptor(new UIWindowAdapter());
    }

    unsafe static void SetupCLRRedirectionAddComponent()
    {
        var arr = typeof(GameObject).GetMethods();
        foreach (var i in arr)
        {
            if (i.Name == "AddComponent" && i.GetGenericArguments().Length == 1)
            {
                appdomain.RegisterCLRMethodRedirection(i, AddComponent);
            }
        }
    }

    unsafe static void SetupCLRRedirectionGetComponent()
    {
        var arr = typeof(GameObject).GetMethods();
        foreach (var i in arr)
        {
            if (i.Name == "GetComponent" && i.GetGenericArguments().Length == 1)
            {
                appdomain.RegisterCLRMethodRedirection(i, GetComponent);
            }
        }
    }

    unsafe static StackObject* AddComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
    {
        //CLR重定向的说明请看相关文档和教程，这里不多做解释
        ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;

        var ptr = __esp - 1;
        //成员方法的第一个参数为this
        GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
        if (instance == null)
            throw new NullReferenceException();
        __intp.Free(ptr);

        var genericArgument = __method.GenericArguments;
        //AddComponent应该有且只有1个泛型参数
        if (genericArgument != null && genericArgument.Length == 1)
        {
            var type = genericArgument[0];
            object res;
            if (type is CLRType)
            {
                //Unity主工程的类不需要任何特殊处理，直接调用Unity接口
                res = instance.AddComponent(type.TypeForCLR);
            }
            else
            {
                //热更DLL内的类型比较麻烦。首先我们得自己手动创建实例
                var ilInstance = new ILTypeInstance(type as ILType, false);//手动创建实例是因为默认方式会new MonoBehaviour，这在Unity里不允许
                                                                           //接下来创建Adapter实例
                var clrInstance = instance.AddComponent<MonoBehaviourAdapter.Adaptor>();
                //unity创建的实例并没有热更DLL里面的实例，所以需要手动赋值
                clrInstance.ILInstance = ilInstance;
                clrInstance.AppDomain = __domain;
                //这个实例默认创建的CLRInstance不是通过AddComponent出来的有效实例，所以得手动替换
                ilInstance.CLRInstance = clrInstance;

                res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance

                clrInstance.Awake();//因为Unity调用这个方法时还没准备好所以这里补调一次
                clrInstance.OnEnable();//因为Unity调用这个方法时还没准备好所以这里补调一次
            }

            return ILIntepreter.PushObject(ptr, __mStack, res);
        }

        return __esp;
    }

    unsafe static StackObject* GetComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
    {
        //CLR重定向的说明请看相关文档和教程，这里不多做解释
        ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;

        var ptr = __esp - 1;
        //成员方法的第一个参数为this
        GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
        if (instance == null)
            throw new NullReferenceException();
        __intp.Free(ptr);

        var genericArgument = __method.GenericArguments;
        //GetComponent应该有且只有1个泛型参数
        if (genericArgument != null && genericArgument.Length == 1)
        {
            var type = genericArgument[0];
            object res = null;
            if (type is CLRType)
            {
                //Unity主工程的类不需要任何特殊处理，直接调用Unity接口
                res = instance.GetComponent(type.TypeForCLR);
            }
            else
            {
                //因为所有DLL里面的MonoBehaviour实际都是这个Component，所以我们只能全取出来遍历查找
                var clrInstances = instance.GetComponents<MonoBehaviourAdapter.Adaptor>();
                for (int i = 0; i < clrInstances.Length; i++)
                {
                    var clrInstance = clrInstances[i];
                    if (clrInstance.ILInstance != null)//ILInstance为null, 表示是无效的MonoBehaviour，要略过
                    {
                        if (clrInstance.ILInstance.Type == type)
                        {
                            res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance
                            break;
                        }
                    }
                }
            }

            return ILIntepreter.PushObject(ptr, __mStack, res);
        }

        return __esp;
    }
}

